/**
 * Model package.
 */
package fr.umlv.m2.ig.model;

import java.util.ArrayList;
import java.util.List;

import fr.umlv.m2.ig.model.StationsTableModel.DisplayWay;
import fr.umlv.m2.ig.model.event.SearchEvent;
import fr.umlv.m2.ig.model.event.SearchListener;
import fr.umlv.m2.ig.model.event.StationEvent;
import fr.umlv.m2.ig.model.event.StationListener;
import fr.umlv.m2.ig.model.event.TrainEvent;
import fr.umlv.m2.ig.model.event.TrainListener;

/**
 * This abstract class defines the Beauheur model.
 * 
 * @author Gambouele Bertrand.
 * @author Viravout Alexandre.
 */
public abstract class AbstractBeauheurModel implements BeauheurModel {

  /**
   * Listeners of stations.
   */
	protected final List<StationListener> stationsListeners;
	
	/**
	 * Listeners of trains.
	 */
	protected final List<TrainListener> trainListeners;
	
	/**
	 * Listeners of search.
	 */
	protected final List<SearchListener> searchListeners;
	
	/**
	 * Fire using to display stations by line.
	 */
	private boolean firingSearchDisplayByLine;
	
	/**
	 * Fire using to display stations according a name.
	 */
	private boolean firingSearchDisplayByName;
	
	/**
	 * Fire using when station state change.
	 */
	private boolean firingStateChanged;
	
	/**
	 * Fire using when train move.
	 */
	private boolean firingTrainEvent;
	
	/**
	 * Initialize AbstractBeauheurModel attributes.
	 */
	public AbstractBeauheurModel() {	  
	  this.stationsListeners = new ArrayList<StationListener>();
	  this.trainListeners = new ArrayList<TrainListener>();
	  this.searchListeners = new ArrayList<SearchListener>();
  }

	@Override
	public void addStationListener(StationListener stationListener) {
		this.stationsListeners.add(stationListener);
	}
	
	@Override
	public void addTrainListener(TrainListener trainsListener) {
		this.trainListeners.add(trainsListener);
	}
	
	@Override
	public void removeStationListener(StationListener stationListener) {
		if (this.firingStateChanged) {
			throw new IllegalStateException("Can't remove listeners while fire is using them");
		}
		
		this.stationsListeners.remove(stationListener);
	}
	
	@Override
	public void removeTrainListener(TrainListener trainsListener) {
		if (this.firingTrainEvent) {
			throw new IllegalStateException("Can't remove listeners while fire is using them");
		}
		
		this.trainListeners.remove(trainsListener);
	}
	
	@Override
	public void addSearchByLineListener(SearchListener searchListener) {
    this.searchListeners.add(searchListener);
	}
	
	@Override
	public void removeSearchByLineListener(SearchListener searchListener) {
	  if(this.firingSearchDisplayByLine) {
      throw new IllegalStateException("Can't remove listeners while fire is using them");
    }
    this.searchListeners.remove(searchListener);
	}
  
  @Override
  public void addSearchByNameListener(SearchListener searchListener) {
    this.searchListeners.add(searchListener);
  }
  
  @Override
  public void removeSearchByNameListener(SearchListener searchListener) {
    if(this.firingSearchDisplayByName) {
      throw new IllegalStateException("Can't remove listeners while fire is using them");
    }
    this.searchListeners.remove(searchListener);
  }
	
	protected void fireSearchDisplayByLine(String line) {
	  try {
	    this.firingSearchDisplayByLine = true;
	    
	    SearchEvent event = new SearchEvent(this, line, DisplayWay.LINE);
	    for(SearchListener searchListener : this.searchListeners) {
	      searchListener.updateSearch(event);
	    }
	    
	  } finally {
	    this.firingSearchDisplayByLine = false;
	  }
	}
	
	protected void fireSearchDisplayByName(String name) {
	  try {
      this.firingSearchDisplayByName = true;
      
      SearchEvent event = new SearchEvent(this, name, DisplayWay.NAME);
      for(SearchListener searchListener : this.searchListeners) {
        searchListener.updateSearch(event);
      }
      
    } finally {
      this.firingSearchDisplayByName = false;
    }
	}

	/**
	 * Fires station according changing.
	 * 
	 * @param station the station to fire.
	 */
	protected void fireStateChanged(Station station) {
		try {
			firingStateChanged = true;
			
			StationEvent event = new StationEvent(this, station);
			
			for (StationListener listener : stationsListeners) {
				listener.stateChanged(event);
			}
		} finally {
			firingStateChanged = false;
		}
	}

	/**
	 * Fires listeners for moved trains.
	 * 
	 * @param movedTrains
	 */
	protected void fireTrainsMoved(List<Train> movedTrains) {
		try {
			firingTrainEvent = true;
			
			TrainEvent ev = new TrainEvent(this, movedTrains);
			
			for (TrainListener listener : trainListeners) {
				listener.trainsMoved(ev);
			}
		} finally {
			firingTrainEvent = false;
		}
	}
	
	/**
	 * Fires listeners for an added train.
	 * 
	 * @param train Added train.
	 */
	protected void fireTrainAdded(Train train) {
		try {
			firingTrainEvent = true;
			
			TrainEvent event = new TrainEvent(this, train);
			
			for (TrainListener listener : trainListeners) {
				listener.trainAdded(event);
			}
		} finally {
			firingTrainEvent = false;
		}
	}
	
	/**
	 * Fires listeners for a removed train.
	 * 
	 * @param train Removed train.
	 */
	protected void fireTrainRemoved(Train train) {
		try {
			firingTrainEvent = true;
			
			TrainEvent event = new TrainEvent(this, train);
			
			for (TrainListener listener : trainListeners) {
				listener.trainRemoved(event);
			}
		} finally {
			firingTrainEvent = false;
		}
	}
	
	/**
	 * Fires listeners for a removed train.
	 * 
	 * @param train Removed train.
	 */
	protected void fireTrainsListReseted() {
		try {
			firingTrainEvent = true;
			
			TrainEvent event = new TrainEvent(this);
			
			for (TrainListener listener : trainListeners) {
				listener.trainsListReseted(event);
			}
		} finally {
			firingTrainEvent = false;
		}
	}
}
